/** @jsxImportSource @emotion/react */
import { FC, useEffect, useRef, MouseEvent, TouchEvent } from "react";
import { css } from "@emotion/react";
import styled from "@emotion/styled";
import { TinyColor } from "../utils";

import { TPropsComp, TCoords } from "./types";

// Styled components
const Container = styled.div`
  position: relative;
  height: 100%;
  width: 100%;
  border-radius: 4px;
  background-image: url();
  background-image: linear-gradient(
    to right,
    #ff0000 0%,
    #ff9900 10%,
    #cdff00 20%,
    #35ff00 30%,
    #00ff66 40%,
    #00fffd 50%,
    #0066ff 60%,
    #3200ff 70%,
    #cd00ff 80%,
    #ff0099 90%,
    #ff0000 100%
  );
`;

const Background = styled.div`
  position: absolute;
  height: 100%;
  width: 100%;
  border-radius: 4px;
  background-image: url();
  background-image: linear-gradient(
    to right,
    #ff0000 0%,
    #ff9900 10%,
    #cdff00 20%,
    #35ff00 30%,
    #00ff66 40%,
    #00fffd 50%,
    #0066ff 60%,
    #3200ff 70%,
    #cd00ff 80%,
    #ff0099 90%,
    #ff0000 100%
  );
`;

const Pointer = styled.span<{ left: number; backgroundColor: string }>`
  position: absolute;
  top: -3px;
  display: block;
  height: 14px;
  width: 14px;
  padding: 1px 0;
  border-radius: 50%;
  margin-left: -7px;
  border: solid 2px #ffffff;
  cursor: grab;
  left: ${(props) => props.left}%;
  background-color: ${(props) => props.backgroundColor};
`;

const Ribbon: FC<TPropsComp> = ({ color, onChange, setChange }) => {
	const node = useRef<HTMLDivElement>(null);

	const removeListeners = () => {
		window.removeEventListener("mousemove", onDrag);
		window.removeEventListener("mouseup", onDragEnd);
	};

	const removeTouchListeners = () => {
		setChange(false);

		window.removeEventListener("touchmove", onTouchMove);
		window.removeEventListener("touchend", onTouchEnd);
	};

	useEffect(() => {
		return () => {
			removeListeners();
			removeTouchListeners();
		};
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, []);

	const onMouseDown = (e: MouseEvent) => {
		e.preventDefault();
		const x = e.clientX;
		const y = e.clientY;

		pointMoveTo({
			x,
			y,
		});

		window.addEventListener("mousemove", onDrag);
		window.addEventListener("mouseup", onDragEnd);
	};

	const onDrag = (e: any) => {
		const x = e.clientX;
		const y = e.clientY;

		pointMoveTo({
			x,
			y,
		});
	};

	const onDragEnd = (e: any) => {
		const x = e.clientX;
		const y = e.clientY;

		pointMoveTo({
			x,
			y,
		});

		setChange(false);

		removeListeners();
	};

	const onTouchStart = (e: TouchEvent) => {
		if (e.cancelable) {
			e.preventDefault();
		}

		if (e.touches.length !== 1) {
			return;
		}

		removeTouchListeners();

		const x = e.targetTouches[0].clientX;
		const y = e.targetTouches[0].clientY;

		pointMoveTo({ x, y });

		window.addEventListener("touchmove", onTouchMove, { passive: false });
		window.addEventListener("touchend", onTouchEnd, { passive: false });
	};

	const onTouchMove = (e: any) => {
		if (e.cancelable) {
			e.preventDefault();
		}

		const x = e.targetTouches[0].clientX;
		const y = e.targetTouches[0].clientY;

		pointMoveTo({
			x,
			y,
		});
	};

	const onTouchEnd = () => {
		removeTouchListeners();
	};

	const pointMoveTo = (coords: TCoords) => {
		const rect = node?.current?.getBoundingClientRect();
		if (!rect) return;
		const width = rect.width;
		let left = coords.x - rect.left;
		left = Math.max(0, left);
		left = Math.min(left, width);

		const huePercent = left / width;
		const hue = huePercent * 360;

		color.hue = hue;
		onChange(color);
	};

	const hueHsv = {
		h: color.hue,
		s: 1,
		v: 1,
	};

	const hueColor = new TinyColor(hueHsv).toHexString();

	const hue = color.hue;
	const per = (hue / 360) * 100;

	return (
		<Container ref={node} onMouseDown={onMouseDown} onTouchStart={onTouchStart}>
			<Background />
			<Pointer left={per} backgroundColor={hueColor} />
			<div
				css={css`
          position: absolute;
          height: 100%;
          width: 100%;
          cursor: grab;
        `}
			/>
		</Container>
	);
};

export default Ribbon;
